home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / bbs_soft / flist26.zip / FLIST.C next >
Text File  |  1993-03-05  |  36KB  |  1,130 lines

  1. /*
  2.  
  3. FLIST -- Generates file lists based on privilege levels and keys
  4.  
  5. Version 2.6   (3/05/93)
  6.  
  7. Written by Bob Quinlan of Austin, Texas, USA
  8. Sysop of Red October at 512-834-2593 (1:382/111)
  9. Special thanks to David Powers for suggestions and code.
  10.  
  11. Copyright 1993 by Bob Quinlan
  12.  
  13. Compatible with Maximus 2.00 and 2.01
  14.  
  15.  
  16. This program generates a list of files that a user with a particular
  17. privilege level and set of keys could access.  It is intended to make
  18. the procedure independent of changes to the structure of your file
  19. areas.  Instead of specifying which areas should be included you just
  20. specify what sort of user you want the list to apply to.
  21.  
  22. You can also create lists of new files.  Just use /D to specify how many
  23. days of files you want to list.
  24.  
  25.     /Afile      Area file.  You can use this to specify a path to
  26.                 AREA.DAT or to refer to another file (although it must
  27.                 follow the AREA.DAT format).  The filename must be
  28.                 included along with the path.  Defaults to AREA.DAT in
  29.                 the current directory.
  30.  
  31.     /B          Include barricaded areas.  If this switch is not
  32.                 included no barricaded areas will be included.
  33.  
  34.     /C          Include comments from the FILES.BBS file lists.  By
  35.                 default only files and their descriptions are included.
  36.  
  37.                 Using this switch has one side-effect!  Normally area
  38.                 headers are only generated for areas that have files in
  39.                 them.  /C will cause them to be generated for any area
  40.                 that has comments as well.  Do not use /C if you only
  41.                 want areas that have files in them to appear!  (I
  42.                 recommend not using /C with /D so that you don't show
  43.                 lots of apparently empty file areas.)
  44.  
  45.     /Ddays      Days.  Excludes all files more than this many days old.
  46.                 Use this switch to create lists of new files.  You
  47.                 cannot use both this and the /M switch at the same time.
  48.  
  49.     /Ffile      Output file.  The pathname of the file you want the file
  50.                 list written to.  Defaults to FLIST.TXT in the current
  51.                 area.
  52.  
  53.     /HAfile     Area header file.  The contents of this file will be
  54.                 added to the report for each area header.  There are
  55.                 several sequences you can embed in the file that have
  56.                 special meanings.  All of the codes explained under /HS
  57.                 are allowed except for %C and %B.
  58.  
  59.                 If no area header file is specified a default header
  60.                 will be used.
  61.  
  62.     /HBfile     Beginning header file.  The contents of this file will
  63.                 be added to the beginning of the report.  There are
  64.                 several sequences you can embed in the file that have
  65.                 special meanings.  The codes %D, %T, and %A (explained
  66.                 under /HS) are allowed.
  67.  
  68.     /HEfile     End header file.  The contents of this file will be
  69.                 added to the end of the report.  There are several
  70.                 sequences you can embed in the file that have special
  71.                 meanings.  The codes %F, %S, %D, %T, and %A (explained
  72.                 under /HS) are allowed.
  73.  
  74.     /HSfile     Area summary header file.  The contents of this file
  75.                 will be added to the report after each area.  There are
  76.                 several sequences you can embed in the file that have
  77.                 special meanings:
  78.  
  79.                     %N will be replaced with the area name.
  80.                     %I will be replaced with the area information.
  81.                     %C will be replaced with the area file count.
  82.                     %B will be replaced with the area file size total (bytes).
  83.                     %F will be replaced with the overall file count.
  84.                     %S will be replaced with the overall file size total.
  85.                     %D will be replaced with the report date.
  86.                     %T will be replaced with the report time.
  87.                     %A will be replaced with the age of the files
  88.                        included (or marked) in the report.  Do not use
  89.                        this except with the /D or /M command line
  90.                        parameters!
  91.  
  92.                     Any of these may be followed by an L, R, or C
  93.                     immediately followed by a number.  These will left,
  94.                     right, or center justify the value in a field of the
  95.                     specified width.
  96.  
  97.                     You can also follow any of the above with an NF or
  98.                     with an Fx.  NF stands for "no fill" and has no
  99.                     effect (see examples).  Fx stands for "fill" and
  100.                     will cause the entire field to be replaced by an
  101.                     equivalent number of the character 'x'.  This is
  102.                     useful when drawing boxes.
  103.  
  104.     /Icode      International country code.  This allows you to override
  105.                 the system's country code and pick another format.  Give
  106.                 the DOS country code for the country you want to use.
  107.  
  108.     /Kkeys      Keys to use for access to locked areas.  Keys are
  109.                 represented by 1-8 and A-X.  An asterisk (*) turns on
  110.                 all of the keys.  Multiple keys can be specified in one
  111.                 /K argument (such as /K12B).
  112.  
  113.     /Mdays      Mark new files.  Marks all files less than or equal to
  114.                 this many days old.  You cannot use both this and the /D
  115.                 switch at the same time.
  116.  
  117.     /N          No-wrap.  Leave long description lines intact.  The
  118.                 default is to wrap them.
  119.  
  120.     /O          Include offline files.  By default files that are listed
  121.                 but not present are not included in the list.
  122.  
  123.     /Ppriv      Privilege to use for access to areas.  Only the first
  124.                 letter of the level name is required.  Legal values are:
  125.                     Twit
  126.                     Disgrace
  127.                     Limited
  128.                     Normal
  129.                     Worthy
  130.                     Privil
  131.                     Favored
  132.                     Extra
  133.                     Clerk
  134.                     AsstSysop
  135.                     Sysop
  136.                     Hidden
  137.                 Defaults to Normal.
  138.  
  139.     /T          Truncate long description lines.  The default is to wrap
  140.                 them.
  141.  
  142.     /Vcount     Volume.  'count' is the number of files each dot stands
  143.                 for in the progress indicator.  Zero will turn off all
  144.                 progress displays.
  145.  
  146.     /Wcolumn    Wrap column.  Sets line wrapping to the specified column
  147.                 relative to the left margin.
  148.  
  149. Let me show you an example of how to use the %N and %I parameters in an
  150. area header file (defined with the /HA switch).  The purpose of those
  151. parameters is to let you build boxes that will conform to the size of
  152. the text produced by %N and %I.  Here is an example of a simple header
  153. box using these parameters:
  154.  
  155. ┌──%NF───┬──%IF───┐
  156. │  %NNF  │  %INF  │
  157. └──%NF───┴──%IF───┘
  158.  
  159. Note that the characters following the %NF and %IF look like a part of
  160. the regular text, but are actually the characters that will be used to
  161. fill out the parameters to the desired length.
  162.  
  163. This will expand to something that looks like this:
  164.  
  165. ┌───────┬───────────────────────────────────────────┐
  166. │  MAX  │  Maximus BBS files and related utilities  │
  167. └───────┴───────────────────────────────────────────┘
  168.  
  169. Now you can see why the apparently useless NF modifier can be useful. It
  170. pads out the shorter embedded codes so that they visually match those in
  171. the surrounding lines.  The following would produce exactly the same
  172. result as the previous example, but it is a lot harder to figure out how
  173. everything will match up:
  174.  
  175. ┌──%NF───┬──%IF───┐
  176. │  %N  │  %I  │
  177. └──%NF───┴──%IF───┘
  178.  
  179. Here is an example using fixed width fields.  This also right justifies
  180. the area name and left justifies the area information:
  181.  
  182. ┌─%NR9F──┬─%IL50F──┐
  183. │ %NR9NF │ %IL50NF │
  184. └─%NR9F──┴─%IL50F──┘
  185.  
  186. Which will expand to something that looks like this:
  187.  
  188. ┌───────────┬────────────────────────────────────────────────────┐
  189. │       MAX │ Maximus BBS files and related utilities            │
  190. └───────────┴────────────────────────────────────────────────────┘
  191.  
  192.  
  193. FLIST returns the following ERRORLEVELS:
  194.     0 = success
  195.     1 = bad command line parameter
  196.     2 = unable to open AREA.DAT
  197.     3 = unable to open header file
  198.     4 = unable to open output file
  199.     5 = not enough memory to run
  200.     6 = string overflow processing header
  201.  
  202. NOTICE:  You may use, copy, and distribute this program freely as long
  203. as you insure that both the executable and the documentation (.DOC)
  204. files are included in the distribution package.  The source code does
  205. not need to be included.  You may modify this program and document, so
  206. long as reasonable credit is given to the original author if a
  207. substantial portion of the original remains intact.  The author is not
  208. responsible for any losses which may occur either directly or indirectly
  209. as a result of using this program.
  210.  
  211. This program uses the Maximus structures written by Scott J. Dudley.
  212.  
  213. HISTORY:
  214. Version 2.6   (3/05/93) -- Added support for international date formats.
  215.                            FLIST now reads the country code and uses the
  216.                            appropriate formats automatically.  The /I
  217.                            options can be used to override the system's
  218.                            country code.
  219. Version 2.5   (1/18/93) -- Added the /M option to mark new files.  Fixed
  220.                            counting of files not present in the list.
  221. Version 2.4   (1/10/93) -- Added the /HS option to allow customized area
  222.                            summary headers in the report.  Added new
  223.                            embedded variables for total and area file
  224.                            counts and file sizes.  Fixed a bug that
  225.                            occurred when inappropriate replacement codes
  226.                            were used in a header.
  227. Version 2.3   (1/10/93) -- Added /W parameter to allow line-wrapping to
  228.                            different lengths.  List "offline" as the
  229.                            size for offline file entries.
  230. Version 2.2  (11/04/92) -- Fixed minor date offsets caused by default
  231.                            timezone and daylight savings adjustments.
  232.                            Added the /V switch to limit screen output.
  233.                            /V0 replaces the /Q switch.
  234. Version 2.1   (8/26/92) -- Added the /Q switch to eliminate the progress
  235.                            display.  Now returns different errorlevels
  236.                            for each possible error.  Fixed problem with
  237.                            headers before comments.
  238. Version 2.0   (8/23/92) -- Added more error checking.
  239. Version 1.9   (8/22/92) -- Added a progress display.  Fixed a memory
  240.                            corruption bug.
  241. Version 1.8   (8/20/92) -- Added fixed width fields with left, right,
  242.                            and centered justification for embedded
  243.                            variables.  Changed the previous codes for
  244.                            embedded variables!  Removed built-in help to
  245.                            reduce memory requirements.
  246. Version 1.7   (8/10/92) -- Added embedded variables to the begin and end
  247.                            headers.  Note: This eliminated the automatic
  248.                            file age comment that used to be included for
  249.                            newfile lists.
  250. Version 1.6   (8/10/92) -- Added the /HA option to allow customized area
  251.                            headers in the report.  This is an expansion
  252.                            of an idea and code by David Powers.
  253. Version 1.5   (8/09/92) -- Added the /HB and /HE options to allow
  254.                            customized beginning and end text in the
  255.                            report.  Added /? help option to print the
  256.                            parameter list.  Added a clearer explanation
  257.                            of /C's effect on area headers.
  258. Version 1.4   (8/07/92) -- Added the /C option to include file list
  259.                            comments.  Added code to recognize free
  260.                            switches so they do not show up in the
  261.                            descriptions.  Fixed error if first entry in
  262.                            AREA.DAT did not include a file area.
  263. Version 1.3   (8/06/92) -- Read the file list filename from AREA.DAT
  264.                            instead of assuming FILES.BBS.
  265. Version 1.2   (8/02/92) -- Added days header for new files list.
  266. Version 1.1   (7/30/92) -- Added file age cutoff.
  267. Version 1.0   (6/30/92) -- Original release.  Written in Borland C.
  268.  
  269. Large memory model
  270. */
  271.  
  272.  
  273. #include <ctype.h>
  274. #include <dos.h>
  275. #include <fcntl.h>
  276. #include <io.h>
  277. #include <share.h>
  278. #include <stdio.h>
  279. #include <stdlib.h>
  280. #include <string.h>
  281. #include <sys\stat.h>
  282. #include <time.h>
  283. #include <mstruct.h>  /*  Maximus structures by Scott J. Dudley  */
  284.  
  285.  
  286. #define VERSION     "2.6"
  287. #define DATE        "3/05/93"
  288.  
  289. #define MAXWIDTH    79
  290. #define MAXDATE     16
  291. #define MAXPATH     128
  292. #define MAXLINE     256
  293. #define MAXDESC     4096
  294.  
  295. #define DEFINDENT   31
  296.  
  297. #define UNITED_STATES   001
  298.  
  299. #ifndef FALSE
  300.     #define FALSE 0
  301.     #define TRUE  1
  302. #endif
  303.  
  304.  
  305. struct _arealist
  306. {
  307.     char    name[40];
  308.     char    info[80];
  309.     char    path[80];
  310.     char    filesbbs[80];
  311.     sword   priv;
  312.     dword   locks;
  313.     byte    barricade;
  314.     struct _arealist *next;
  315. };
  316.  
  317.  
  318. typedef struct _arealist __arealist;
  319. typedef struct _area __area;
  320.  
  321.  
  322. void process_args(int argc, char *argv[]);
  323. sword get_priv(char priv);
  324. dword get_keys(char *key_list);
  325. __arealist *read_areas(void);
  326. void process_header(char type, char *hfile, char *name, char *info, FILE *out);
  327. void process_entry(char *entry, char *path, char *name, char *info,
  328.     int *header, FILE *out);
  329.  
  330.  
  331. char    areafile[MAXPATH] = {"area.dat"};
  332. char    hareafile[MAXPATH] = {""};
  333. char    hbeginfile[MAXPATH] = {""};
  334. char    hendfile[MAXPATH] = {""};
  335. char    hsummaryfile[MAXPATH] = {""};
  336. char    outfile[MAXPATH] = {"flist.txt"};
  337. sword   priv = NORMAL;
  338. dword   keys = 0x00000000;
  339. int     barricade = FALSE;
  340. int     comment = FALSE;
  341. long    days = -1L;
  342. int     mark = FALSE;
  343. int     offline = FALSE;
  344. int     wrap = DEFINDENT;
  345. int     volume = 1;
  346. int     vloop;
  347. long    age = -1L;
  348. int     total_count = 0;
  349. int     area_count;
  350. long    total_bytes = 0L;
  351. long    area_bytes;
  352. int     country_code = 0;
  353. struct COUNTRY cp;
  354.  
  355.  
  356. int main(int argc, char *argv[])
  357. {
  358.  
  359.     char        filespath[MAXPATH];
  360.  
  361.     int         header;
  362.  
  363.     FILE        *out;
  364.     FILE        *files;
  365.  
  366.     __arealist  *areas;
  367.     __arealist  *cur;
  368.     __arealist  *prev;
  369.  
  370.     char        line[MAXDESC];
  371.  
  372.  
  373.     printf("FLIST %s -- Copyright 1993 by Bob Quinlan (%s)\n\n", VERSION, DATE);
  374.     printf("Special thanks to David Powers for suggestions and code.\n\n");
  375.  
  376.     process_args(argc, argv);
  377.  
  378.     areas = read_areas();
  379.  
  380.     if ((out = _fsopen(outfile, "wt", SH_DENYWR)) == NULL)
  381.     {
  382.         fprintf(stderr, "Unable to open %s\n", outfile);
  383.         exit(4);
  384.     }
  385.  
  386.     /*  Get country-specific formatting information  */
  387.     if (country(country_code, &cp) == NULL)
  388.     {
  389.         cp.co_date = 0;
  390.         strcpy(cp.co_curr, "$");
  391.         strcpy(cp.co_thsep, ",");
  392.         strcpy(cp.co_desep, ".");
  393.         strcpy(cp.co_dtsep, "-");
  394.         strcpy(cp.co_tmsep, ":");
  395.         cp.co_currstyle = 0;
  396.         cp.co_digits = 2;
  397.         cp.co_time = 0;
  398.         cp.co_case = 0x1160cf5L;
  399.         strcpy(cp.co_dasep, ",");
  400.     }
  401.  
  402.     if (hbeginfile[0] != '\0')
  403.     {
  404.         process_header('b', hbeginfile, NULL, NULL, out);
  405.     }
  406.  
  407.     cur = areas;
  408.     while (cur != NULL)
  409.     {
  410.         if ((priv >= (cur->priv)) && (((~keys)&(cur->locks)) == 0) &&
  411.             ((cur->barricade == 0) || (barricade)))
  412.         {
  413.             if (volume)
  414.             {
  415.                 vloop = -1;
  416.                 printf("%s -- %s", cur->name, cur->info);
  417.             }
  418.             strcpy(filespath, cur->filesbbs);
  419.             if (filespath[0] == '\0')
  420.             {
  421.                 strcpy(filespath, cur->path);
  422.                 strcat(filespath, "files.bbs");
  423.             }
  424.             if ((files = _fsopen(filespath, "rt", SH_DENYNONE)) != NULL)
  425.             {
  426.                 area_count = 0;
  427.                 area_bytes = 0L;
  428.                 header = FALSE;
  429.                 while (fgets(line, MAXDESC, files) != NULL)
  430.                 {
  431.                     process_entry(line, cur->path, cur->name, cur->info,
  432.                         &header, out);
  433.                     if (volume)
  434.                     {
  435.                         if ((vloop = (vloop+1)%volume) == 0)
  436.                         {
  437.                             printf(".");
  438.                         }
  439.                     }
  440.                 }
  441.                 fclose(files);
  442.                 if (header)
  443.                 {
  444.                     if (hsummaryfile[0] != '\0')
  445.                     {
  446.                         process_header('s', hsummaryfile, cur->name,
  447.                             cur->info, out);
  448.                     }
  449.                 }
  450.             }
  451.             if (volume)
  452.             {
  453.                 printf("\n");
  454.             }
  455.         }
  456.         /*  Increment to next link and free previous link  */
  457.         prev = cur;
  458.         cur = cur->next;
  459.         free(prev);
  460.     }
  461.  
  462.     if (hendfile[0] != '\0')
  463.     {
  464.         process_header('e', hendfile, NULL, NULL, out);
  465.     }
  466.  
  467.     fclose(out);
  468.  
  469.     return 0;
  470. }
  471.  
  472.  
  473. void process_args(int argc, char *argv[])
  474. {
  475.     int i;
  476.  
  477.     for (i = 1; i < argc; i++)
  478.     {
  479.         if ((argv[i][0] == '/') || (argv[i][0] == '-'))
  480.         {
  481.             switch (tolower(argv[i][1]))
  482.             {
  483.                 case 'a':  /*  area.dat  */
  484.                     strncpy(areafile, argv[i]+2, MAXPATH);
  485.                     break;
  486.                 case 'b':  /*  include barricaded areas  */
  487.                     barricade = TRUE;
  488.                     break;
  489.                 case 'c':  /*  include file list comment lines  */
  490.                     comment = TRUE;
  491.                     break;
  492.                 case 'd':  /*  days (of file date)  */
  493.                     mark = FALSE;
  494.                     days = atol(argv[i]+2);
  495.                     age = time(NULL)-(days*24*60*60);
  496.                     break;
  497.                 case 'f':  /*  output file  */
  498.                     strncpy(outfile, argv[i]+2, MAXPATH);
  499.                     break;
  500.                 case 'h':  /*  headers  */
  501.                     switch(tolower(argv[i][2]))
  502.                     {
  503.                         case 'a':
  504.                             strncpy(hareafile, argv[i]+3, MAXPATH);
  505.                             break;
  506.                         case 'b':
  507.                             strncpy(hbeginfile, argv[i]+3, MAXPATH);
  508.                             break;
  509.                         case 'e':
  510.                             strncpy(hendfile, argv[i]+3, MAXPATH);
  511.                             break;
  512.                         case 's':
  513.                             strncpy(hsummaryfile, argv[i]+3, MAXPATH);
  514.                             break;
  515.                         default:
  516.                             fprintf(stderr, "Unknown switch: %s\n", argv[i]);
  517.                             exit(1);
  518.                             break;
  519.                     }
  520.                     break;
  521.                 case 'i':  /*  international country code  */
  522.                     country_code = atoi(argv[i]+2);
  523.                     break;
  524.                 case 'k':  /*  keys  */
  525.                     keys |= get_keys(argv[i]+2);
  526.                     break;
  527.                 case 'm':  /*  mark new files  */
  528.                     mark = TRUE;
  529.                     days = atol(argv[i]+2);
  530.                     age = time(NULL)-(days*24*60*60);
  531.                     break;
  532.                 case 'n':  /*  no-wrap  */
  533.                     wrap = -1;
  534.                     break;
  535.                 case 'o':  /*  offline files allowed  */
  536.                     offline = TRUE;
  537.                     break;
  538.                 case 'p':  /*  privilege  */
  539.                     priv = get_priv(argv[i][2]);
  540.                     if (priv < TWIT)
  541.                     {
  542.                         priv = TWIT;
  543.                         fprintf(stderr, "Invalid privilege: %c\n", argv[i][2]);
  544.                     }
  545.                     break;
  546.                 case 't':  /*  truncate lines  */
  547.                     wrap = -2;
  548.                     break;
  549.                 case 'v':  /*  volume  */
  550.                     volume = max(atoi(argv[i]+2), 0);
  551.                     break;
  552.                 case 'w':  /*  wrap column  */
  553.                     wrap = min(max(atoi(argv[i]+2), 0),
  554.                         MAXWIDTH-((MAXWIDTH-DEFINDENT)/2));
  555.                     break;
  556.                 default:
  557.                     fprintf(stderr, "Unknown switch: %s\n", argv[i]);
  558.                     exit(1);
  559.                     break;
  560.             }
  561.         }
  562.         else
  563.         {
  564.             fprintf(stderr, "Not a switch: %s\n", argv[i]);
  565.             exit(1);
  566.         }
  567.     }
  568. }
  569.  
  570.  
  571. sword get_priv(char priv)
  572. {
  573.     char    priv_name[] = {"tdlnwpfecash"};
  574.     sword   priv_code[] =
  575.             {
  576.                 TWIT,
  577.                 DISGRACE,
  578.                 LIMITED,
  579.                 NORMAL,
  580.                 WORTHY,
  581.                 PRIVIL,
  582.                 FAVORED,
  583.                 EXTRA,
  584.                 CLERK,
  585.                 ASSTSYSOP,
  586.                 SYSOP,
  587.                 HIDDEN
  588.             };
  589.  
  590.     char    *where;
  591.  
  592.     where = strchr(priv_name, tolower(priv));
  593.     if (where == NULL)
  594.     {
  595.         return TWIT-1;
  596.     }
  597.  
  598.     return priv_code[(int)(where-priv_name)];
  599. }
  600.  
  601.  
  602. dword get_keys(char *key_list)
  603. {
  604.     char    *key;
  605.     dword   mask = 0x00000000;
  606.     int     keylen;
  607.     int     i;
  608.  
  609.     key = strupr(strdup(key_list));
  610.  
  611.     keylen = strlen(key);
  612.     for (i = 0; i < keylen; i++)
  613.     {
  614.         if (key[i] == '*')
  615.         {
  616.             mask = 0xFFFFFFFFL;
  617.             break;
  618.         }
  619.         else if ((key[i] >= '1') && (key[i] <= '8'))
  620.         {
  621.             mask |= 0x00000001L<<(key[i]-'1');
  622.         }
  623.         else if ((key[i] >= 'A') && (key[i] <= 'X'))
  624.         {
  625.             mask |= 0x00000100L<<(key[i]-'A');
  626.         }
  627.     }
  628.  
  629.     free(key);
  630.  
  631.     return mask;
  632. }
  633.  
  634.  
  635. __arealist *read_areas(void)
  636. {
  637.     FILE        *areadat;
  638.     __area      area;
  639.     __arealist  *tmp;
  640.     __arealist  *first;
  641.     __arealist  *cur = NULL;
  642.     dword       slen;
  643.     dword       offset = 0;
  644.  
  645.     if ((areadat = _fsopen(areafile, "rb", SH_DENYNONE)) == NULL)
  646.     {
  647.         fprintf(stderr, "Unable to open %s\n", areafile);
  648.         exit(2);
  649.     }
  650.  
  651.     while (fread(&area, sizeof(__area), 1, areadat) > 0)
  652.     {
  653.         if (offset == 0)
  654.         {
  655.             slen = (dword)(area.struct_len);
  656.         }
  657.         if (area.filepath[0] != '\0')
  658.         {
  659.             if ((tmp = (__arealist *)malloc(sizeof(__arealist))) == NULL)
  660.             {
  661.                 fprintf(stderr, "Out of memory!\n");
  662.                 exit(5);
  663.             }
  664.             if (cur == NULL)
  665.             {
  666.                 first = tmp;
  667.             }
  668.             else
  669.             {
  670.                 cur->next = tmp;
  671.             }
  672.             cur = tmp;
  673.             strcpy(cur->name, (char *)area.name);
  674.             strcpy(cur->info, (char *)area.fileinfo);
  675.             strcpy(cur->path, (char *)area.filepath);
  676.             strcpy(cur->filesbbs, (char *)area.filesbbs);
  677.             cur->priv = area.filepriv;
  678.             cur->locks = area.filelock;
  679.             cur->barricade = (area.filebar[0] != '\0');
  680.         }
  681.         offset += slen;
  682.         if (fseek(areadat, offset, SEEK_SET) != 0)
  683.         {
  684.             break;
  685.         }
  686.     }
  687.  
  688.     cur->next = NULL;
  689.     fclose(areadat);
  690.  
  691.     return first;
  692. }
  693.  
  694.  
  695. void process_header(char type, char *hfile, char *name, char *info, FILE *out)
  696. {
  697.     FILE    *h;
  698.     char    buf[MAXLINE];
  699.     char    tbuf[MAXLINE];
  700.     char    filler[MAXLINE];
  701.     int     flen;
  702.     char    *param;
  703.     int     plen;
  704.     int     fwidth;
  705.     char    *stopper;
  706.     char    c;
  707.     int     j, k;
  708.  
  709.     if ((h = _fsopen(hfile, "rt", SH_DENYNONE)) == NULL)
  710.     {
  711.         fprintf(stderr, "Unable to open %s\n", hfile);
  712.         exit(3);
  713.     }
  714.     /*  Read data from the header file  */
  715.     while (fgets(buf, MAXLINE, h) != NULL)
  716.     {
  717.         /*  Process special replacement characters  */
  718.         param = buf;
  719.         while ((param = strchr(param, '%')) != NULL)
  720.         {
  721.             filler[0] = '\0';
  722.             plen = 2;
  723.             /*  Check the basic parameter type  */
  724.             switch (tolower(param[1]))
  725.             {
  726.                 case 'a':  /*  age  */
  727.                     if (days != -1L)
  728.                     {
  729.                         ltoa(days, filler, 10);
  730.                     }
  731.                     break;
  732.                 case 'd':  /*  date  */
  733.                     _strdate(filler);
  734.                     break;
  735.                 case 't':  /*  time  */
  736.                     _strtime(filler);
  737.                     break;
  738.             }
  739.             /*  Do not allow totals in beginning headers  */
  740.             if (type != 'b')
  741.             {
  742.                 switch (tolower(param[1]))
  743.                 {
  744.                     case 'f':  /*  total file count  */
  745.                         itoa(total_count, filler, 10);
  746.                         break;
  747.                     case 's':  /*  file size total  */
  748.                         ltoa(total_bytes, filler, 10);
  749.                         break;
  750.                 }
  751.             }
  752.             /*  Only allow area name and information in area and summary headers  */
  753.             if ((type == 'a') || (type == 's'))
  754.             {
  755.                 switch (tolower(param[1]))
  756.                 {
  757.                     case 'n':  /*  name  */
  758.                         strcpy(filler, name);
  759.                         break;
  760.                     case 'i':  /*  information  */
  761.                         strcpy(filler, info);
  762.                         break;
  763.                 }
  764.             }
  765.             /*  Only allow area count and area bytes in summary headers  */
  766.             if (type == 's')
  767.             {
  768.                 switch (tolower(param[1]))
  769.                 {
  770.                     case 'c':  /*  area file count  */
  771.                         itoa(area_count, filler, 10);
  772.                         break;
  773.                     case 'b':  /*  area file size total (bytes)  */
  774.                         ltoa(area_bytes, filler, 10);
  775.                         break;
  776.                 }
  777.             }
  778.  
  779.             /*  If any replacements were found...  */
  780.             if (filler[0] != '\0')
  781.             {
  782.                 /*  Process width values if present  */
  783.                 c = tolower(param[plen]);
  784.                 if (strchr("lrc", c) != NULL)
  785.                 {
  786.                     ++plen;
  787.                     /*  Convert the width value and find where it stops  */
  788.                     fwidth = (int)strtol(param+plen, &stopper, 10);
  789.                     plen += (int)(stopper-(param+plen));
  790.                     /*  If the field is wider than the filler string...  */
  791.                     if (fwidth >= MAXLINE)
  792.                     {
  793.                         fprintf(stderr, "Width %d too wide!  Ignored.\n",
  794.                             fwidth);
  795.                     }
  796.                     /*  If the field is wider than the current data...  */
  797.                     else if (strlen(filler) < fwidth)
  798.                     {
  799.                         switch (c)
  800.                         {
  801.                             case 'l':  /*  left justify  */
  802.                                 /*  Pad on the right  */
  803.                                 for (j = strlen(filler); j < fwidth; j++)
  804.                                 {
  805.                                     filler[j] = ' ';
  806.                                 }
  807.                                 break;
  808.                             case 'r':  /*  right justify  */
  809.                                 flen = strlen(filler);
  810.                                 /*  Shift the string right  */
  811.                                 for (j = 1; j <= flen; j++)
  812.                                 {
  813.                                     filler[fwidth-j] = filler[flen-j];
  814.                                 }
  815.                                 /*  Pad on the left  */
  816.                                 for (j = 0; j < fwidth-flen; j++)
  817.                                 {
  818.                                     filler[j] = ' ';
  819.                                 }
  820.                                 break;
  821.                             case 'c':  /*  center  */
  822.                                 flen = strlen(filler);
  823.                                 /*  Pad on the right  */
  824.                                 k = (fwidth-flen+1)/2;
  825.                                 for (j = 1; j <= k; j++)
  826.                                 {
  827.                                     filler[fwidth-j] = ' ';
  828.                                 }
  829.                                 /*  Shift the string to center  */
  830.                                 for (j = 1; j <= flen; j++)
  831.                                 {
  832.                                     filler[fwidth-k-j] = filler[flen-j];
  833.                                 }
  834.                                 /*  Pad on the left  */
  835.                                 k = (fwidth-flen)/2;
  836.                                 for (j = 0; j < k; j++)
  837.                                 {
  838.                                     filler[j] = ' ';
  839.                                 }
  840.                                 break;
  841.                         }
  842.                         /*  Terminate the adjusted string  */
  843.                         filler[fwidth] = '\0';
  844.                     }
  845.                 }
  846.  
  847.                 /*  Process fill character if present  */
  848.                 if (tolower(param[plen]) == 'f')
  849.                 {
  850.                     strset(filler, param[++plen]);
  851.                     ++plen;
  852.                 }
  853.                 /*  Skip "no fill" parameter if present  */
  854.                 else if (strncmpi(param+plen, "nf", 2) == 0)
  855.                 {
  856.                     plen += 2;
  857.                 }
  858.  
  859.                 /*  Perform the replacment  */
  860.                 if ((strlen(buf)+strlen(filler)-plen) >= MAXLINE)
  861.                 {
  862.                     fprintf(stderr, "String overflow!\n");
  863.                     exit(6);
  864.                 }
  865.                 param[0] = '\0';
  866.                 strcpy(tbuf, buf);
  867.                 strcat(tbuf, filler);
  868.                 strcat(tbuf, param+plen);
  869.                 strcpy(buf, tbuf);
  870.                 param = param+strlen(filler);
  871.             }
  872.             else
  873.             {
  874.                 param++;
  875.             }
  876.         }
  877.  
  878.         /*  Output header  */
  879.         fputs(buf, out);
  880.     }
  881.  
  882.     fclose(h);
  883. }
  884.  
  885.  
  886. void process_entry(char *entry, char *path, char *name, char *info,
  887.       int *header, FILE *out)
  888. {
  889.     char    filepath[MAXPATH];
  890.     int     file;
  891.  
  892.     long            fsize;
  893.     struct ftime    ftimep;
  894.     struct tm         ftm;
  895.     char            datestr[MAXDATE];
  896.     int             fdata;
  897.  
  898.     char    line[MAXDESC];
  899.     char    fill[MAXWIDTH+1];
  900.     char    extra[MAXWIDTH+1];
  901.     char    filemark;
  902.     char    *descrip;
  903.     char    *end;
  904.     char    *ch;
  905.     int     length;
  906.     int     nameline;
  907.     int     i;
  908.  
  909.     filemark = ' ';
  910.     strcpy(line, entry);
  911.  
  912.     /*  Trim newline  */
  913.     end = strchr(line, '\n');
  914.     if (end != NULL)
  915.     {
  916.         end[0] = '\0';
  917.     }
  918.     else
  919.     {
  920.         end = strchr(line, '\0');
  921.     }
  922.  
  923.     /*  Check for comment lines  */
  924.     if (strchr(" -", line[0]) != NULL)
  925.     {
  926.         if (comment)
  927.         {
  928.             /*  Output area header if not already done  */
  929.             if (!(*header))
  930.             {
  931.                 if (hareafile[0] != '\0')
  932.                 {
  933.                     process_header('a', hareafile, name, info, out);
  934.                 }
  935.                 else
  936.                 {
  937.                     fprintf(out, "\n\n    Area %s -- %s\n\n", name, info);
  938.                 }
  939.                 *header = TRUE;
  940.             }
  941.  
  942.             /*  Output comment line  */
  943.             fprintf(out, "%s\n", line);
  944.         }
  945.     }
  946.     else
  947.     {
  948.         /*  Parse into filename and description, skipping switches  */
  949.         descrip = line;
  950.         do
  951.         {
  952.             ch = strpbrk(descrip, " \t");
  953.             if (ch == NULL)
  954.             {
  955.                 descrip = end;
  956.             }
  957.             else
  958.             {
  959.                 ch[0] = '\0';
  960.                 i = strspn(++ch, " \t");
  961.                 descrip = ch+i;
  962.             }
  963.         } while (descrip[0] == '/');
  964.  
  965.         /*  Get file size and date  */
  966.         strcpy(filepath, path);
  967.         strcat(filepath, line);
  968.         if ((file = sopen(filepath, O_RDONLY, SH_DENYNONE)) == -1)
  969.         {
  970.             if (offline)
  971.             {
  972.                 fdata = FALSE;
  973.             }
  974.             else
  975.             {
  976.                 return;
  977.             }
  978.         }
  979.         else
  980.         {
  981.             fsize = filelength(file);
  982.             getftime(file, &ftimep);
  983.             close(file);
  984.             fdata = TRUE;
  985.  
  986.             /*  Generate date string  */
  987.             switch (cp.co_date)
  988.             {
  989.                 case 0:  /*  month, day, year  */
  990.                     sprintf(datestr, "%2.2u%s%2.2u%s%2.2u",
  991.                         ftimep.ft_month,
  992.                         cp.co_dtsep,
  993.                         ftimep.ft_day,
  994.                         cp.co_dtsep,
  995.                         (ftimep.ft_year+80)%100);
  996.                     break;
  997.                 case 1:  /*  day, month, year  */
  998.                     sprintf(datestr, "%2.2u%s%2.2u%s%2.2u",
  999.                         ftimep.ft_day,
  1000.                         cp.co_dtsep,
  1001.                         ftimep.ft_month,
  1002.                         cp.co_dtsep,
  1003.                         (ftimep.ft_year+80)%100);
  1004.                     break;
  1005.                 case 2:  /*  year, month, day  */
  1006.                     sprintf(datestr, "%2.2u%s%2.2u%s%2.2u",
  1007.                         (ftimep.ft_year+80)%100,
  1008.                         cp.co_dtsep,
  1009.                         ftimep.ft_month,
  1010.                         cp.co_dtsep,
  1011.                         ftimep.ft_day);
  1012.                     break;
  1013.             }
  1014.  
  1015.             /*  Calculate datestamp offset  */
  1016.             timezone = 0L;
  1017.             daylight = 0;
  1018.             memset(&ftm, 0, sizeof(struct tm));
  1019.             ftm.tm_sec = ftimep.ft_tsec*2;
  1020.             ftm.tm_min = ftimep.ft_min;
  1021.             ftm.tm_hour = ftimep.ft_hour;
  1022.             ftm.tm_mday = ftimep.ft_day;
  1023.             ftm.tm_mon = ftimep.ft_month-1;
  1024.             ftm.tm_year = ftimep.ft_year+80;
  1025.             if (mktime(&ftm) < age)
  1026.             {
  1027.                 if (!mark)
  1028.                 {
  1029.                     return;
  1030.                 }
  1031.             }
  1032.             else
  1033.             {
  1034.                 if (mark)
  1035.                 {
  1036.                     filemark = '*';
  1037.                 }
  1038.             }
  1039.         }
  1040.  
  1041.         /*  Output area header if not already done  */
  1042.         if (!(*header))
  1043.         {
  1044.             if (hareafile[0] != '\0')
  1045.             {
  1046.                 process_header('a', hareafile, name, info, out);
  1047.             }
  1048.             else
  1049.             {
  1050.                 fprintf(out, "\n\n    Area %s -- %s\n\n", name, info);
  1051.             }
  1052.             *header = TRUE;
  1053.         }
  1054.  
  1055.         /*  Update file counts  */
  1056.         total_count++;
  1057.         area_count++;
  1058.  
  1059.         /*  Output file entry  */
  1060.         if (fdata)
  1061.         {
  1062.             /*  Update file sizes  */
  1063.             total_bytes += fsize;
  1064.             area_bytes += fsize;
  1065.             /*  Output file data  */
  1066.             fprintf(out, "%-12s %7lu %s%c ", line, fsize, datestr, filemark);
  1067.         }
  1068.         else
  1069.         {
  1070.             fprintf(out, "%-12s offline         %c ", line, filemark);
  1071.         }
  1072.         /*  Output description  */
  1073.         switch (wrap)
  1074.         {
  1075.             case -2:  /*  truncate  */
  1076.                 descrip[MAXWIDTH-DEFINDENT] = '\0';
  1077.                 fprintf(out, "%s\n", descrip);
  1078.                 break;
  1079.             case -1:  /*  no-wrap  */
  1080.                 fprintf(out, "%s\n", descrip);
  1081.                 break;
  1082.             default:  /*  wrap  */
  1083.                 length = MAXWIDTH-DEFINDENT;
  1084.                 fill[0] = '\0';
  1085.                 nameline = TRUE;
  1086.                 /*  Set initial length  */
  1087.                 while (strlen(descrip) > length)
  1088.                 {
  1089.                     for (i = length; i > length/2; i--)
  1090.                     {
  1091.                         if (strchr(" \t", descrip[i]) != NULL)
  1092.                         {
  1093.                             break;
  1094.                         }
  1095.                     }
  1096.                     /*  If there are no breaks...  */
  1097.                     if (i == length/2)
  1098.                     {
  1099.                         strncpy(extra, descrip, length);
  1100.                         extra[length] = '\0';
  1101.                         fprintf(out, "%s%s\n", fill, extra);
  1102.                         descrip += length;
  1103.                     }
  1104.                     else
  1105.                     {
  1106.                         descrip[i++] = '\0';
  1107.                         fprintf(out, "%s%s\n", fill, descrip);
  1108.                         descrip += i;
  1109.                         while (strchr(" \t", descrip[0]) != NULL)
  1110.                         {
  1111.                             descrip++;
  1112.                         }
  1113.                     }
  1114.                     if (nameline == TRUE)
  1115.                     {
  1116.                         /*  Recalculate length  */
  1117.                         length = MAXWIDTH-wrap;
  1118.                         memset(fill, ' ', wrap);
  1119.                         fill[wrap] = '\0';
  1120.                         nameline = FALSE;
  1121.                     }
  1122.                 }
  1123.                 fprintf(out, "%s%s\n", fill, descrip);
  1124.                 break;
  1125.         }
  1126.     }
  1127. }
  1128.  
  1129.  
  1130.